package morenoapp.components.thinkgraph.node;

import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolox.nodes.PComposite;
import java.awt.Color;
import java.awt.Paint;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import morenoapp.MorenoApp;
import morenoapp.components.thinkgraph.model.DisplayFilterListener;
import morenoapp.components.thinkgraph.model.DisplayFilter;
import morenoapp.model.data.MorenoAnswerFace;
import morenoapp.model.data.MorenoQuestionFace;
import morenoapp.model.data.MorenoQuestionItemFace;
import morenoapp.model.data.MorenoRespondentFace;

/**
 * 
 * @author dmitry.mamonov
 */
public class RespondentNode extends PComposite implements WithTooltip {
    /**
     * Респондент которому соответствует данное визуальное представление.
     */
    MorenoRespondentFace respondentFace;

    PPath back;

    /**
     * Текстовая метка содержащая номер респондента по порядку,
     * опционально его имя и другую информацию.
     */
    PText text = new PText();

    /**
     * Корректировка указывает на круг в социограмме,
     * в котором должен распологаться данный узел.
     */
    VectorNode correction = null;

    /**
     * Слушатели изменения положения данного узла (связи слушают
     * изменения положения узла для корректировки координат начала
     * и окончания связи.
     */
    Set<PositionListener> positionListerers = new HashSet();

    /**
     * Ссылка на текущий фильтр, фильтр может запретить показ текущего
     * узла или изменить его конфигурацию.
     */
    DisplayFilter filter;

    /**
     * Цвет фона которым следует отображать данный узел.
     */
    Color defaultPaint = Color.WHITE;

    /**
     *
     * @param _respondentFace респондент для отображения.
     * @param _filter фильтр задающий параметры отображения.
     */
    public RespondentNode(MorenoRespondentFace _respondentFace, DisplayFilter _filter) {
        super();
        this.respondentFace = _respondentFace;
        this.filter = _filter;

        this.filter.addDisplayFilterLisneter(new DisplayFilterListener() {
            public void onDisplayFilterChanged() {
                configureDisplay();
            }
        });

        configureDisplay();
    }

    private void configureCorrectionVector() {
        //display correcting vectors, if specified
        if (this.indexOfChild(correction) >= 0) {
            this.removeChild(correction);
        }
        if (filter.isDisplayCorrectionVectors()) {
            //determine target round (ring number which holds current node correctly)
            int currentRing = getCurrentRing();
            int correctRing = getCorrectRing();
            if (currentRing != correctRing) {
                double distanceToCenter = getDistanceToCenter();
                double distanceToCorrectPoint = (correctRing + 0.5) * filter.getRingStep();
                double correctionVectorLength = distanceToCorrectPoint - distanceToCenter;
                double normalizedDirectionX = 1.0;
                double normalizedDirectionY = 0.0;
                if (distanceToCenter > 1) {
                    normalizedDirectionX = getX() / distanceToCenter;
                    normalizedDirectionY = getY() / distanceToCenter;
                }
                correction = new VectorNode(getX(), getY(), getX() + (normalizedDirectionX * correctionVectorLength), getY() + (normalizedDirectionY * correctionVectorLength), 5.0f, 5.0f, null);
                this.addChild(correction);
            }
        }
    }

    private void configureDisplay() {

        //add text label as child if it's not presented
        if (this.indexOfChild(text) < 0) {
            text.setPaint(Color.WHITE);
            addChild(text);
        }

        //display respondent name if it's specified by filter
        if (filter.isDisplayNames()) {
            text.setText(this.getRespondentFace().getName());
        } else {
            text.setText("" + (this.respondentFace.getIndex() + 1));
        }

        //configure node background coresponding to filter
        if (this.indexOfChild(back) >= 0) {
            removeChild(back);
        }

        if (filter.isDisplaySex()) {
            switch (this.getRespondentFace().getSex()) {
                case MALE:
                    back = PPath.createRectangle(-15, -15, 30, 30);
                    break;
                case FEMALE:
                    back = PPath.createEllipse(-15, -15, 30, 30);
                    break;
                default:
                    back = PPath.createEllipse(-5, -5, 10, 10);
                    break;
            }
            defaultPaint = this.getRespondentFace().getSex().getColor().brighter().brighter().brighter();
        } else {
            back = PPath.createEllipse(-15, -15, 30, 30);
            defaultPaint = Color.WHITE;
        }
        addChild(0, back);

        //original draw is transparent
        setPaint(null);

        //setup current node position
        //Point2D position = this.respondentFace.getGraphPosition();
        //this.setX(position.getX());
        //this.setY(position.getY());

        //configure correction
        configureCorrectionVector();

        //redisplay
        invalidatePaint();
    }

    public double getDistanceToCenter(){
        double x = getX();
        double y = getY();
        return Math.sqrt(x*x+y*y);
    }

    public int getCurrentRing(){
        int currentRing = (int) Math.floor(getDistanceToCenter()/filter.getRingStep());
        return Math.min(3, currentRing);
    }

    public int getCorrectRing(){
        return (int) respondentFace.getMetricIndividualStatus();
    }

    public void configureRelationships(Map<MorenoRespondentFace, RespondentNode> _nodesMap) {
        List<MorenoQuestionFace> questionList = MorenoApp.getApplicationData().getMorenoModelProxy().getQuestionsList();
        for (MorenoQuestionFace question : questionList) {
            MorenoAnswerFace answer = respondentFace.getAnswerByQuestion(question);
            for (MorenoQuestionItemFace questionItem : question.getQuestionItemsList()) {
                MorenoRespondentFace target = answer.getAnswerItemByQuestionItem(questionItem).getTarget();
                if (target != null) {
                    RespondentNode targetNode = _nodesMap.get(target);
                    if (targetNode == null) {
                        throw new RuntimeException("Missed target node!"); //NOI18N
                    }
                    RelationshipNode relationship = new RelationshipNode(this, targetNode, question, filter);
                    addChild(relationship);
                }
            }
        }
    }

    @Override
    public boolean setX(double x) {
        boolean result = super.setX(x);
        text.setX(x - text.getWidth() / 2);
        back.setX(x - back.getWidth() / 2);
        //this.getRespondentFace().setGraphPosition(new Point2D.Double(getX(), getY()));
        firePositionChange();
        return result;
    }

    @Override
    public boolean setY(double y) {
        boolean result = super.setY(y);
        text.setY(y - text.getHeight() / 2);
        back.setY(y - back.getHeight() / 2);
        //this.getRespondentFace().setGraphPosition(new Point2D.Double(getX(), getY()));
        firePositionChange();
        return result;
    }

    @Override
    public void setPaint(Paint _newPaint) {
        Paint newPaint = _newPaint != null ? _newPaint : defaultPaint;
        super.setPaint(newPaint);
        text.setPaint(newPaint);
        back.setPaint(newPaint);
    }

    public MorenoRespondentFace getRespondentFace() {
        return respondentFace;
    }

    public void addPositionListener(PositionListener _listener) {
        this.positionListerers.add(_listener);
    }

    public void removePositionLIstener(PositionListener _listeListener) {
        this.positionListerers.remove(_listeListener);
    }

    private void firePositionChange() {
        //do homework
        configureCorrectionVector();
        
        //fire listeners
        for (PositionListener listener : positionListerers) {
            listener.onPositionChange(this);
        }
    }

    public String getTooltipText() {
        return respondentFace.getName() + "/" + respondentFace.getSex().getPlainString()+ //NOI18N
                "\n rank = "+respondentFace.getMetricIndividualStatus(); //NOI18N
    }
}
